#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>

// 父进程读子进程写---父进程关闭写端,子进程关闭读端
static void test_pipe1()
{
    // 1.父进程先调用pipe函数,创建匿名管道获取读写端的文件描述符
    int pipefd[2];
    memset(pipefd, 0, sizeof(pipefd));

    int ret = pipe(pipefd);
    if (ret != 0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.父进程调用fork创建子进程, 在进程创建中子进程会继承父进程的struct file,
    // 所以父子进程的pipefd中文件描述符会标定同一个匿名管道
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        exit(2);
    }

    // 3.因为管道通信是半双工的, 所以需要根据需求关闭父子进程各一个fd

    if (0 == id)
    {
        // child code
        close(pipefd[0]);

        int n = 10;
        while (n--)
        {
            char buffer[1024];
            sprintf(buffer, "hello father, I am child: %d", n);
            write(pipefd[1], buffer, strlen(buffer));

            sleep(1);
        }

        close(pipefd[1]);
        exit(0);
    }

    // father code
    close(pipefd[1]);

    while (true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));

        ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = '\0';

            printf("chlid# %s\n", buffer);
        }
        else if (0 == n)
        {
            printf("child exit\n");
            break;
        }
        else
        {
            perror("read");
            exit(3);
        }
    }

    close(pipefd[0]);
    exit(0);
}

void sigpipe_handler(int signum)
{
    printf("收到信号: %d\n", signum);
    exit(4);
}

// 父进程写子进程读---父进程关闭读端,子进程关闭写端
static void test_pipe2()
{
    // 1.pipe
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret < 0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.fork
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        exit(2);
    }

    if (0 == id)
    {
        // child code
        close(pipefd[1]); // 子进程关闭写端

        int n = 3;
        while (n--)
        {
            char buffer[1024];

            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = '\0';
                printf("father# %s\n", buffer);

                // sleep(1);
            }
            else if (0 == n)
            {
                printf("father close\n");
                exit(0);
            }
            else
            {
                perror("read");
                exit(3);
            }
        }

        close(pipefd[0]);
        exit(0);
    }

    // father code
    close(pipefd[0]); // 父进程关闭读端

    // 父进程对SIGPIPE信号进行捕获
    signal(SIGPIPE, sigpipe_handler);

    // 父进程一直往匿名管道中写入数据
    int count = 0;
    while (true)
    {
        char buffer[1024];
        sprintf(buffer, "I am father, pid: %d, count %d\n", getpid(), count++);

        ssize_t n = write(pipefd[1], buffer, strlen(buffer));
        if (n > 0)
        {
            // printf("write n>0: n: %d\n",n);
            usleep(5000);
        }
        else if (0 == n)
        {
            printf("write 0==n: n: %d\n", n);
            break;
        }
        else
        {
            perror("write");
            exit(5);
        }
    }

    close(1);
    exit(0);
}

// 测试管道的大小,父进程一直不读,子进程一直写
static void test_pipe_size()
{
    // 1.pipe
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret < 0)
    {
        perror("pipe");
        exit(1);
    }

    // 2.fork
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork");
        exit(2);
    }

    if (0 == id)
    {
        // child code
        close(pipefd[0]); // 子进程关闭写端
        // 子进程一直往匿名管道中写
        
        int count = 0; // 记录写入了多少个字节数
        while (true)
        {
            char one_byte = '$';
            ssize_t n = write(pipefd[1], &one_byte, 1);

            if (n > 0)
            {
                printf("count: %d bytes\n", ++count);
            }
        }

        close(pipefd[1]);
        exit(0);
    }

    // father code
    close(pipefd[1]); // 父进程关闭读端
    // 父进程一直不从匿名管道中读
    for(;;){}

    close(pipefd[0]);
    exit(0);
}

int main(int argc, char *argv[], char *env[])
{
    // test_pipe1();
    // test_pipe2();
    test_pipe_size();
    return 0;
}
